package bkhech.generator.mybatis.core;

import bkhech.generator.mybatis.Constant;
import bkhech.generator.mybatis.domian.MyGenConfig;
import cn.hutool.extra.cglib.CglibUtil;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.*;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * get load and generate
 *
 * @author guowm
 * @date 2022/8/9
 */
@Slf4j
public class MybatisGen {

    public static void generate() {
        System.setProperty("file.encoding", "UTF-8");
        try {
            List<String> warnings = new ArrayList<>();
            Configuration config = getConfiguration(warnings);
            Context context = config.getContexts().get(0);

            fillConfigTemplate(context);

            boolean overwrite = true;
            DefaultShellCallback callback = new DefaultShellCallback(overwrite);
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);

            myBatisGenerator.generate(null);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据模板初始化 MyBatis Generator 配置
     *
     * @param warnings
     * @return
     * @throws IOException
     * @throws XMLParserException
     */
    private static Configuration getConfiguration(List<String> warnings) throws IOException, XMLParserException {
        final ClassLoader classLoader = MybatisGen.class.getClassLoader();
        ConfigurationParser cp = new ConfigurationParser(warnings);
        final InputStream stream = classLoader.getResourceAsStream(Constant.DEFAULT_GENERATOR_CONFIG_TEMPLATE);
        return cp.parseConfiguration(stream);
    }

    /**
     * fill custom config template
     *
     * @param context
     */
    private static void fillConfigTemplate(Context context) {
        final MyGenConfig customGenConfig = GenConfigLoader.load();
        setGen(customGenConfig, context);
        setJdbc(customGenConfig, context);
        setTable(customGenConfig, context);
    }

    /**
     * set table configuration
     *
     * @param myGenConfig
     * @param context
     */
    private static void setTable(MyGenConfig myGenConfig, Context context) {
        final List<TableConfiguration> tableConfs = context.getTableConfigurations();
        if (tableConfs.isEmpty()) {
            throw new RuntimeException("请在模板中配置<table>标签相关项");
        }
        final TableConfiguration tableConf = tableConfs.get(0);


        final List<MyGenConfig.Table> tables = myGenConfig.getTables();
        if (tables.isEmpty()) {
            throw new RuntimeException("没有执行生成逻辑，待生成的表为空");
        }

        tableConfs.clear();

        // maybe has bug in there
        // private Map<IgnoredColumn, Boolean> ignoredColumns;
        // private List<IgnoredColumnPattern> ignoredColumnPatterns = new ArrayList<IgnoredColumnPattern>();

        tables.forEach(item -> {
            final TableConfiguration cloneTableConf = new TableConfiguration(context);
            //copy original property
            CglibUtil.copy(tableConf, cloneTableConf);
            // deep copy
            cloneTableConf.setColumnRenamingRule(tableConf.getColumnRenamingRule());
            cloneTableConf.setDomainObjectRenamingRule(tableConf.getDomainObjectRenamingRule());
            tableConf.getProperties().forEach((key, value) -> cloneTableConf.addProperty((String) key, (String) value));
            tableConf.getColumnOverrides().forEach(cloneTableConf::addColumnOverride);

            // table name and object name
            cloneTableConf.setTableName(item.getTableName());
            cloneTableConf.setDomainObjectName(item.getObjectName());
            tableConfs.add(cloneTableConf);
        });
    }

    /**
     * set jdbc configuration
     *
     * @param myGenConfig
     * @param context
     */
    private static void setJdbc(MyGenConfig myGenConfig, Context context) {
        final JDBCConnectionConfiguration jdbcConnConf = context.getJdbcConnectionConfiguration();
        jdbcConnConf.setDriverClass(myGenConfig.getJdbc().getDriverClass());
        jdbcConnConf.setConnectionURL(myGenConfig.getJdbc().getConnectionURL());
        jdbcConnConf.setUserId(myGenConfig.getJdbc().getUserId());
        jdbcConnConf.setPassword(myGenConfig.getJdbc().getPassword());
    }

    /**
     * 设置 model、 xml、 mapper 相关配置
     *
     * @param myGenConfig
     * @param context
     */
    private static void setGen(MyGenConfig myGenConfig, Context context) {
        final File projectFile = new File(".").getAbsoluteFile();
        final JavaModelGeneratorConfiguration javaModelConf = context.getJavaModelGeneratorConfiguration();
        javaModelConf.setTargetProject(new File(projectFile, myGenConfig.getGen().getModel().getTarget()).getPath());
        javaModelConf.setTargetPackage(myGenConfig.getGen().getModel().getPkg());

        final SqlMapGeneratorConfiguration sqlMapConf = context.getSqlMapGeneratorConfiguration();
        sqlMapConf.setTargetProject(new File(projectFile, myGenConfig.getGen().getMap().getTarget()).getPath());
        sqlMapConf.setTargetPackage(myGenConfig.getType() + "." + myGenConfig.getGen().getMap().getPkg());

        final JavaClientGeneratorConfiguration javaClientConf = context.getJavaClientGeneratorConfiguration();
        javaClientConf.setTargetProject(new File(projectFile, myGenConfig.getGen().getClient().getTarget()).getPath());
        javaClientConf.setTargetPackage(myGenConfig.getGen().getClient().getPkg());
    }
}
